#!/bin/bash

#publicly backup a file or folder
#publicly because it will be stored on a non-private git on github (or at least that's the intended purpose) but not by this script, however i wanted this script to remind me that by its name

# --no-clobber
# --update
# if using both --no-clobber and --update, and source is newer(timestamp-wise) but same contents, dest won't be updated at all.

#copies all file contents with permissions unkept but rather using perms for current user:
#all source dirs will be 755 on dest
#all source files will be 644 on dest
#symlink files/dirs will be preserved except their perms which are: lrwxrwxrwx as reported by ls -la, however mc reports the perms of dest file pointed by the symlink instead.
#all this ^ is in preparation for storing these in a git repo. (git knows only 755 and 644 perms btw)
#cp --target-directory=. --no-preserve=mode,ownership --preserve=timestamps,links --parents --no-clobber --verbose --reflink=auto --sparse=auto --recursive --no-dereference -- /tmp/1

#done: add list of ignored files/dirs and maybe they can be relative too
#include the -name or -iname or -path prefix in that list, which will be passed to find

#TODO: add list of included files/dirs

if test -z "$@"; then
	echo "pass a file/dir to backup recursively"
	exit 9
fi

#preferred diff(XXX:you can change):
diff='colordiff -up' #--new-group-format=%<'
#diff="git diff --no-index" #git diff can't actually diff with pipe!
#don't change this:
declare -r realdiff='diff -up'
declare -r commenttoken='#'

ignoredlstfile="ignored.lst"
allignoredraw="$(cat "$ignoredlstfile")"
#echo "$allignored"
allignored=()
#$(
#echo "$allignored" | 
#FIXME: escaped \/  gets converted to / due to 'read'?
while IFS= read eachline ; do
	#remove comments (lines starting with # but whitespace(eg. spaces,tabs etc.) before # are disregarded!)
	eachline="$(sed -e 's/^'"${commenttoken}"'.*//' <<< "$eachline")"
	#remove whitespace?
#	eachline="$(echo -n "$eachline" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
#	eachline="${eachline%%${commenttoken}*}"
	#comments at end of line will leave spaces at end after being removed
#	eachline=${eachline%% }
#	eachline=${eachline/%[[:space:]]/}
#	eachline="$(echo -n "$eachline" | sed -e 's/[[:space:]]*$//')" #includes tabs

	if test -z "$eachline"; then
		continue #ignore now empty lines
	fi

	#done: escape the single quote?
#	echo "'$eachline'"
	allignored+=("$eachline")
#	echo "'$eachline'" >&2
	#add -prune -o -print
	#ignorednames_findparams+=" $eachline -prune -o" #doesn't work because it's piped process
done <<< "$allignoredraw"
#)" 

#find ./1 "${allignored[@]}"
#astext="${allignored[@]}"
astext="$(
for eachline in "${allignored[@]}"; do
	echo "$eachline"
done | sort --
)"
#echo "$astext"
#exit 2
lenbefore="${#astext}"
#allignored="$(sort -u -- <<< "${astext}")"
astextsorted="$(sort -mu -- <<< "${astext}")"
#echo "$astextsorted"

if test "$lenbefore" -ne "${#astextsorted}"; then
	echo "duplicates detected in '$ignoredlstfile'"
	#show the diff to see the duplicates; use colordiff only if exists; fallback to diff or else fail
	while true; do
	actualcmd="${diff%% *}" #that is, strip away the params.
	if ! type "${actualcmd}" >/dev/null 2>&1 ; then
		echo "'$actualcmd' not found in PATH='$PATH'"
		if test "$diff" = "$realdiff"; then #we already tried realdiff? aka last one to try, after $diff
			echo "Can't show you the duplicates, aborting everything..."
			exit 4
		else
			echo "falling back on $realdiff"
		fi
		diff="$realdiff"
	else
		break;
	fi
	done
#	$diff -- <(sort -- <<< "${astext}") <(echo "$astextsorted")
	$diff -- <(echo "${astext}") <(echo "$astextsorted")
	echo "aborting..."
	exit 5
fi


ignorednames_findparams=()
#"$(
#echo "$allignored" | 
#while IFS= read eachline; do
for eachline in "${allignored[@]}"; do
#	echo ".${eachline}."
	if [[ "$eachline" =~ [:print:]*/$ ]]; then
		echo "This line should not end with slash because it won't match(says find): '$eachline'"
		exit 8
	fi

	if [[ "$eachline" =~ / ]]; then
		ignorednames_findparams+=("-path")
	else
		ignorednames_findparams+=("-name")
	fi

	ignorednames_findparams+=("$eachline" "-prune" "-o")
#	echo "-name" #fixed: can be -path when $eachline has / in it
#	echo "$eachline"
#	echo "-prune"
#	echo "-o"
done #<<< "$allignored"
#)")


#done: NOTmake filenames within doublequotes work! OR have one filename per line, removing -name and -path prefixes which means I will have to detect pathnames or just files which means if a file has / escaped as \/  it will be treated as path hmm unless I check for \ ?
#done: allow # within quotes! do not treat it as a comment! So, either completely remove the ability to have comments at end of line (compromise that is) or fix it some other way by actually counting quotes and doublequotes and excluding the one which are escaped - too hard!  so only allow comments at the beginning of line, otherwise treat as filename
#: ${ignorednames_findparams=$ignorednames_findparams}
#ignorednames_findparams=`echo -n $ignorednames_findparams`
#printf '%s\n' "${ignorednames_findparams[@]}"
#echo "${ignorednames_findparams[@]}"
#find ./1 "${ignorednames_findparams[@]}"
#exit 1
#done: don't detect empty lines as duplicates! eg. remove comments and whitespace BEFORE the check!

#echo ".${ignorednames_findparams[@]}."
#echo "$ignorednames_findparams"

#TODO: add list of included files too, not just the ignored(excluded) ones
#...here...
includednames_findparams=()
includednames_findparams+=("-name" "emp" "-print" "-o" "-name" "pmp" "-print") #ok each must have a print!
#includednames_findparams+=("-name" "emp" "-o" "-name" "pmp" ) #won't work without -print for each!
#final param:
#ignorednames_findparams+=("-print")

#find all files/folders/symlinks without diving into symlink folders
#TODO: show to the user  the list of all encountered symlinks folders  just in case they want to dive in too, and not just copy/backup that symlink; also show where symlink points to because likely user wants that folder added to the list
set -vx
abspath="`realpath -e --no-symlinks -- "$@"`"
foundlist="$(find -P -- "$abspath" "${ignorednames_findparams[@]}" \( "${includednames_findparams[@]}" \))"
set +vx
#FIXME: find a way to use -print0 because filenames may contain new line ???? or what, because this would break the names within the *.lst files!!
#TODO: see -fls in man find

#echo "$foundlist"

foundarray=()
while IFS= read eachline ; do
	foundarray+=("$eachline")
done <<< "$foundlist"

#echo "${foundarray[@]}"

#report any symlinks found by find... just in case user may want to add them to list of included files!
for eachline in "${foundarray[@]}"; do
	if test -L "$eachline"; then
		echo "symlink: '$eachline'"
		if test -e "$eachline"; then
			#TODO: only report this(and the symlink) if this dest file/dir isn't in our list of included files/dirs already.; this applies to both dir and file below:
			if test -d "$eachline"; then
				echo -n " to dir:  '"
			else
				echo -n " to file: '"
			fi
			echo "`readlink --canonicalize-existing -- "$eachline"`'"
		else
		  #TODO: this should be a warning
			echo " to inexistent dest: '`readlink --canonicalize-missing -- "$eachline"`'"
		fi
	fi
done

echo END

#declare -a allignored
#cat ignored.lst | read -r -d '' -a allignored
#echo "${allignored[@]}"


# ----------------------- keep this at EOF
# vim: set ts=2:
# ^ to have effect, must have :set modeline  ! try  :set modeline?   to see if it's set;; XXX: don't actually :set modeline  but instead use securemodelines in ~/.vim/bundles/securemodelines/.git  from https://github.com/ciaranm/securemodelines
